import json
import re
import time
from typing import Optional, Dict, Any
import requests


class Config:
    CAPSOLVER_API_KEY = "CAP-147149A8FD7E82B0A83BCD2E317C51AB2D5060780317C5C0488E4DBBFF596609"
    USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0"
    TIMEOUT = 10
    BASE_URL = "https://manatuku.com"


class BaseRequestHandler:
    @staticmethod
    def make_request(
            url: str,
            method: str = "GET",
            headers: Optional[Dict[str, str]] = None,
            cookies: Optional[Dict[str, str]] = None,
            data: Optional[Dict[str, str]] = None,
            proxies: Optional[Dict[str, str]] = None,
            timeout: int = Config.TIMEOUT
    ) -> Optional[requests.Response]:
        """统一的HTTP请求处理方法"""
        default_headers = {
            "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
            "user-agent": Config.USER_AGENT,
        }

        if headers:
            default_headers.update(headers)

        try:
            kwargs = {
                "url": url,
                "headers": default_headers,
                "cookies": cookies,
                "timeout": timeout,
                "proxies": proxies or {},
            }

            if data:
                kwargs["data"] = data

            response = requests.request(method, **kwargs)
            response.raise_for_status()
            return response

        except requests.exceptions.RequestException as e:
            print(f"请求 {url} 时出错: {e}")
            return None


class ManatSuku:
    def __init__(self, manatsuku_session: str = "", aws_waf_token: str = ""):
        self.manatsuku_session = manatsuku_session
        self.aws_waf_token = aws_waf_token
        self.gmo_payment_token = ""
        self.card_company = ""
        self.holder_name = ""
        self.card_number = ""
        self.expire = ""
        self.proxies = {}

    def set_proxies(self, ip: str, port: str, username: str = "", password: str = "") -> None:
        """设置代理"""
        auth_part = f"{username}:{password}@" if username and password else ""
        proxy_url = f"http://{auth_part}{ip}:{port}"
        self.proxies = {"http": proxy_url, "https": proxy_url}

    def get_new(self) -> Optional[requests.Response]:
        """获取支付页面"""
        headers = {
            "referer": f"{Config.BASE_URL}/payments/new",
            "cache-control": "no-cache",
            "pragma": "no-cache",
        }
        cookies = {
            "_manatsuku_session": self.manatsuku_session,
            "aws-waf-token": self.aws_waf_token
        }
        return BaseRequestHandler.make_request(
            f"{Config.BASE_URL}/payments/new",
            headers=headers,
            cookies=cookies,
            proxies=self.proxies
        )

    def get_sign_up(self) -> Optional[requests.Response]:
        """获取注册页面"""
        cookies = {}
        if self.manatsuku_session:
            cookies["_manatsuku_session"] = self.manatsuku_session
        if self.aws_waf_token:
            cookies["aws-waf-token"] = self.aws_waf_token

        return BaseRequestHandler.make_request(
            f"{Config.BASE_URL}/customers/sign_up",
            cookies=cookies,
            proxies=self.proxies
        )

    def submit_payment(self, authenticity_token: str) -> Optional[requests.Response]:
        """提交支付"""
        headers = {
            "referer": f"{Config.BASE_URL}/payments/new",
            "content-type": "application/x-www-form-urlencoded",
            "origin": Config.BASE_URL,
        }

        data = {
            "utf8": "✓",
            "authenticity_token": authenticity_token,
            "payment_credit_card[type]": "Payment::CreditCard",
            "payment_credit_card[card_company]": self.card_company,
            "payment_credit_card[card_number]": self.card_number,
            "payment_credit_card[holder_name]": self.holder_name,
            "payment_credit_card[gmo_payment_token]": self.gmo_payment_token,
        }

        cookies = {
            "aws-waf-token": self.aws_waf_token,
            "_manatsuku_session": self.manatsuku_session,
        }

        return BaseRequestHandler.make_request(
            f"{Config.BASE_URL}/payments",
            method="POST",
            headers=headers,
            cookies=cookies,
            data=data,
            proxies=self.proxies
        )

    def register_customer(
            self,
            authenticity_token: str,
            lastname: str,
            firstname: str,
            email: str,
            password: str
    ) -> Optional[requests.Response]:
        """注册"""
        headers = {
            "content-type": "application/x-www-form-urlencoded",
            "origin": Config.BASE_URL,
            "referer": f"{Config.BASE_URL}/customers",
        }

        data = {
            "utf8": "✓",
            "authenticity_token": authenticity_token,
            "customer[firstkana]": "",
            "customer[lastkana]": "",
            "customer[notification_mail_subscription]": "1",
            "customer[i_am_bot]": "",
            "customer[lastname]": lastname,
            "customer[firstname]": firstname,
            "customer[email]": email,
            "customer[password]": password,
        }

        cookies = {
            "_manatsuku_session": self.manatsuku_session,
            "aws-waf-token": self.aws_waf_token,
        }

        return BaseRequestHandler.make_request(
            f"{Config.BASE_URL}/customers",
            method="POST",
            headers=headers,
            cookies=cookies,
            data=data,
            proxies=self.proxies
        )

    def set_gmo_payment(self, card_number: str, holder_name: str, expire: str, card_company: str) -> bool:
        """设置GMO支付信息"""
        self.card_number = card_number
        self.holder_name = holder_name
        self.card_company = card_company
        self.expire = expire

        self.gmo_payment_token = self._get_gmo_token()
        return bool(self.gmo_payment_token)

    def _get_gmo_token(self) -> Optional[str]:
        """获取GMO令牌"""
        params = {
            "cardno": self.card_number,
            "expire": self.expire,
            "holdername": self.holder_name,
        }

        try:
            response = requests.get(
                "http://121.36.223.161:8000/get_token",
                params=params,
                timeout=Config.TIMEOUT
            )
            response.raise_for_status()
            result = response.json()
            url = result.get("url")

            if not url:
                print("获取令牌URL失败")
                return None

            token_data = self._get_token_from_url(url)
            return token_data.get("tokenObject", {}).get("token") if token_data else None

        except requests.exceptions.RequestException as e:
            print(f"获取GMO令牌失败: {e}")
            return None

    @staticmethod
    def _get_token_from_url(url: str) -> Optional[Dict[str, Any]]:
        """从URL获取令牌"""
        try:
            response = requests.get(url, timeout=Config.TIMEOUT)
            response.raise_for_status()

            content = response.text.strip()
            pattern = r'confirmToken$({.*})$'
            match = re.match(pattern, content)

            if match:
                return json.loads(match.group(1))

            print("响应格式不匹配")
            return None

        except (requests.exceptions.RequestException, json.JSONDecodeError) as e:
            print(f"从URL获取令牌出错: {e}")
            return None

    def _handle_captcha_challenge(self, response_text: str, original_url: str) -> Optional[str]:
        """处理验证码挑战"""
        print("检测到验证码挑战")

        challenge_url = re.search(r'src="(https?://[^"]*challenge\.js)"', response_text)
        key = re.search(r'"key":"([^"]*)"', response_text)
        iv = re.search(r'"iv":"([^"]*)"', response_text)
        context = re.search(r'"context":"([^"]*)"', response_text)

        if not all([challenge_url, key, iv, context]):
            print("提取验证码参数失败")
            return None

        challenge_url = challenge_url.group(1)
        key = key.group(1)
        iv = iv.group(1)
        context = context.group(1)

        try:
            print("正在解决验证码...")
            start_time = time.time()

            result = self._solve_aws_waf(
                original_url,
                challenge_url,
                self.proxies,
                key,
                iv,
                context,

            )

            if result:
                elapsed_time = time.time() - start_time
                print(f"验证码解决耗时: {elapsed_time:.2f}秒")
                return result

            print("验证码解决失败")
            return None

        except TimeoutError as e:
            print("验证码解决超时")
            return None

    @staticmethod
    def _solve_aws_waf(
            site_url: str,
            challenge: str,
            proxies: Dict[str, str],
            aws_key: str = "",
            aws_iv: str = "",
            aws_context: str = "",
            proxy: str = ""
    ) -> Optional[str]:
        """使用Capsolver解决AWS WAF验证"""
        payload = {
            "clientKey": Config.CAPSOLVER_API_KEY,
            "task": {
                "type": "AntiAwsWafTaskProxyLess",
                "websiteURL": site_url,
                "awsChallengeJS": challenge,
                "awsKey": aws_key,
                "awsIv": aws_iv,
                "proxy": proxy,
                "awsContext": aws_context,
            }
        }

        try:
            # 创建任务
            create_res = requests.post(
                "https://api.capsolver.com/createTask",
                json=payload,
                proxies=proxies,
                verify=False,
                timeout=Config.TIMEOUT
            )
            create_res.raise_for_status()
            task_data = create_res.json()
            task_id = task_data.get("taskId")

            if not task_id:
                print("创建任务失败:", create_res.text)
                return None

            print(f"任务已创建: {task_id}. 等待解决方案...")

            # 轮询获取结果
            while True:
                time.sleep(1)
                get_payload = {"clientKey": Config.CAPSOLVER_API_KEY, "taskId": task_id}

                get_res = requests.post(
                    "https://api.capsolver.com/getTaskResult",
                    json=get_payload,
                    proxies=proxies,
                    timeout=Config.TIMEOUT
                )
                get_res.raise_for_status()
                result_data = get_res.json()

                if result_data.get("status") == "ready":
                    return result_data.get("solution", {}).get("cookie")

                if result_data.get("status") == "failed" or result_data.get("errorId"):
                    print("任务失败:", get_res.text)
                    return None

        except requests.exceptions.RequestException as e:
            print(f"解决AWS WAF时出错: {e}")
            return None

    def process_response(self, response: requests.Response) -> Optional[str]:
        """处理响应，获取session和token"""
        set_cookie = response.headers.get("set-cookie", "")

        if not set_cookie:
            print("响应中未找到cookie")
            return None

        session_match = re.search(r"_manatsuku_session=([^;]+)", set_cookie)

        if session_match:
            self.manatsuku_session = session_match.group(1)
            print(f"更新session: {self.manatsuku_session}")
        else:
            print("cookie中未找到session")

        # 返回响应文本供进一步处理
        return response.text

    def get_authenticity_token(self, html_text: str) -> Optional[str]:
        """从HTML中提取authenticity_token"""
        token_match = re.search(
            r'<input type="hidden" name="authenticity_token" value="([^"]+)"',
            html_text
        )

        if token_match:
            token = token_match.group(1)
            print(f"获取到authenticity_token: {token}")
            return token

        print("提取authenticity_token失败")
        return None

    def check_error_page(self, html_text: str) -> Optional[str]:
        """检查错误页面"""
        if "error_explanation" not in html_text:
            print("未检测到错误")
            return None

        error_match = re.search(
            r'<div id="error_explanation">(.*?)</div>',
            html_text,
            re.DOTALL
        )

        if error_match:
            error_message = error_match.group(1).strip()
            print(f"检测到错误: {error_message}")
            return error_message

        print("发现错误说明但未能提取消息")
        return None

    def process_payment_result(self, response: requests.Response) -> bool:
        """处理支付结果"""
        html_text = self.process_response(response)

        if not html_text:
            return False

        error_message = self.check_error_page(html_text)

        if error_message:
            print(f"支付失败: {error_message}")
            return False

        print("支付成功")
        return True

    def process_registration_result(self, response: requests.Response) -> Optional[str]:
        """处理注册结果"""
        html_text = self.process_response(response)

        if not html_text:
            return None

        if self.check_error_page(html_text):
            return None

        print("注册成功")
        return self.manatsuku_session

    def main_payment_flow(self) -> bool:
        """主要支付流程"""
        start_time = time.time()

        # 第一步：获取支付页面
        new_page = self.get_new()

        if not new_page:
            print("获取支付页面失败")
            return False

        print(f"初始页面状态码: {new_page.status_code}")

        # 检查验证码挑战
        if "JavaScript is disabled" in new_page.text:
            captcha_result = self._handle_captcha_challenge(
                new_page.text,
                f"{Config.BASE_URL}/payments/new"
            )

            if not captcha_result:
                print("验证码解决失败")
                return False

            self.aws_waf_token = captcha_result
            print("验证码解决后重试...")
            return self.main_payment_flow()

        # 处理正常响应
        html_text = self.process_response(new_page)

        if not html_text:
            return False

        authenticity_token = self.get_authenticity_token(html_text)

        if not authenticity_token:
            return False

        # 提交支付
        payment_response = self.submit_payment(authenticity_token)

        if not payment_response:
            return False

        # 处理支付结果
        result = self.process_payment_result(payment_response)

        elapsed_time = time.time() - start_time
        print(f"支付流程完成，耗时 {elapsed_time:.2f} 秒")

        return result

    def main_registration_flow(
            self,
            lastname: str,
            firstname: str,
            email: str,
            password: str
    ) -> Optional[str]:
        """主要注册流程"""
        start_time = time.time()

        # 第一步：获取注册页面
        sign_up_page = self.get_sign_up()

        if not sign_up_page:
            print("获取注册页面失败")
            return None

        print(f"初始页面状态码: {sign_up_page.status_code}")

        # 检查验证码挑战
        if "JavaScript is disabled" in sign_up_page.text:
            captcha_result = self._handle_captcha_challenge(
                sign_up_page.text,
                f"{Config.BASE_URL}/customers/sign_up"
            )

            if not captcha_result:
                print("验证码解决失败")
                return None

            self.aws_waf_token = captcha_result
            print("验证码解决后重试...")
            return self.main_registration_flow(lastname, firstname, email, password)

        # 处理正常响应
        html_text = self.process_response(sign_up_page)

        if not html_text:
            return None

        authenticity_token = self.get_authenticity_token(html_text)

        if not authenticity_token:
            return None

        # 提交注册
        registration_response = self.register_customer(
            authenticity_token,
            lastname,
            firstname,
            email,
            password
        )

        if not registration_response:
            return None

        # 处理注册结果
        result = self.process_registration_result(registration_response)

        elapsed_time = time.time() - start_time
        print(f"注册流程完成，耗时 {elapsed_time:.2f} 秒")

        return result

    @staticmethod
    @staticmethod
    def validate_expire_date(expire: str) -> Optional[str]:
        """验证并格式化过期日期"""
        if not re.fullmatch(r"\d{6}", expire):
            print("错误: 过期日期必须是6位数字")
            return None

        if expire[:2] == "20":
            year, month = expire[:4], expire[4:]
        elif expire[2:4] == "20":
            month, year = expire[:2], expire[2:6]
        else:
            print("错误: 过期日期格式不合法 - 必须包含四位年份")
            return None

        # 验证月份
        if not 1 <= int(month) <= 12:
            print(f"错误: 月份 {month} 不合法（必须是01-12）")
            return None

        # 验证年份
        current_year = time.localtime().tm_year
        if not 2000 <= int(year) <= current_year + 20:
            print(f"错误: 年份 {year} 不合法（必须是2000-{current_year + 20}）")
            return None

        # 验证是否已过期
        current_month = time.localtime().tm_mon
        if int(year) == current_year and int(month) < current_month:
            print(f"错误: 信用卡已于 {year}年{month}月 过期")
            return None

        # 返回格式化后的日期 (YYYYMM)
        return f"{year}{month.zfill(2)}"


def main(
        manatsuku_session: str,
        card_number: str,
        holder_name: str,
        expire: str,
        card_company: str,
        proxy_ip: str,
        proxy_port: str,
        proxy_username: str = "",
        proxy_password: str = ""
) -> bool:
    """主支付流程函数"""

    # 验证并格式化过期日期
    formatted_expire = ManatSuku.validate_expire_date(expire)
    if not formatted_expire:
        return False

    print("开始支付流程...")
    start_time = time.time()

    # 初始化API
    manat_api = ManatSuku(manatsuku_session, "")
    manat_api.set_proxies(proxy_ip, proxy_port, proxy_username, proxy_password)

    # 设置GMO支付信息
    if not manat_api.set_gmo_payment(card_number, holder_name, formatted_expire, card_company):
        print("设置GMO支付信息失败")
        return False

    # 执行主支付流程
    result = manat_api.main_payment_flow()

    elapsed_time = time.time() - start_time
    print(f"支付流程总耗时: {elapsed_time:.2f}秒")

    return result


def main_register(
        proxy_ip: str,
        proxy_port: str,
        lastname: str,
        firstname: str,
        email: str,
        password: str,
        proxy_username: str = "",
        proxy_password: str = ""
) -> Optional[str]:
    """主注册流程函数"""

    print("开始注册流程...")
    start_time = time.time()

    # 初始化API
    manat_api = ManatSuku("", "")
    manat_api.set_proxies(proxy_ip, proxy_port, proxy_username, proxy_password)

    # 执行主注册流程
    session_token = manat_api.main_registration_flow(lastname, firstname, email, password)

    elapsed_time = time.time() - start_time
    print(f"注册流程总耗时: {elapsed_time:.2f}秒")

    return session_token


if __name__ == "__main__":
    # 配置代理信息
    PROXY_IP = '24fd2a4e70b435d6.xji.na.novada.pro24fd2a4e70b435d6.xji.na.novada.pro'
    PROXY_PORT = '7777'
    PROXY_USERNAME = 'novada309zkB_ynaP3U-zone-resi-region-jp'
    PROXY_PASSWORD = 'wMiwCpuXV5he'

    # 示例用法 - 支付流程
    PAYMENT_EXAMPLE = False
    if PAYMENT_EXAMPLE:
        SESSION = 'your_session_token'
        CARD_NUMBER = '1234567812345678'
        HOLDER_NAME = 'TEST USER'
        EXPIRE = '202512'  # YYYYMM格式
        CARD_COMPANY = 'VISA'

        payment_result = main(
            SESSION,
            CARD_NUMBER,
            HOLDER_NAME,
            EXPIRE,
            CARD_COMPANY,
            PROXY_IP,
            PROXY_PORT,
            PROXY_USERNAME,
            PROXY_PASSWORD
        )
        print(f"支付结果: {'成功' if payment_result else '失败'}")
    # 示例用法 - 注册流程
    REGISTER_EXAMPLE = True
    if REGISTER_EXAMPLE:
        LASTNAME = 'zhang'
        FIRSTNAME = 'shang'
        EMAIL = 'zhangshang824@gmail.com'
        PASSWORD = '12345678'

        new_session = main_register(
            PROXY_IP,
            PROXY_PORT,
            LASTNAME,
            FIRSTNAME,
            EMAIL,
            PASSWORD,
            PROXY_USERNAME,
            PROXY_PASSWORD
        )
        if new_session:
            print(f"注册成功，获取到的session token: {new_session}")
        else:
            print("注册失败")